home *** CD-ROM | disk | FTP | other *** search
- Subject: v22i045: NN Newsreader, release 6.4, Part10/21
- Newsgroups: comp.sources.unix
- Approved: rsalz@uunet.UU.NET
- X-Checksum-Snefru: e60d6ea7 a14e823a 18b99a38 34670250
-
- Submitted-by: "Kim F. Storm" <storm@texas.dk>
- Posting-number: Volume 22, Issue 45
- Archive-name: nn6.4/part10
-
- #! /bin/sh
- # This is a shell archive. Remove anything before this line, then feed it
- # into a shell via "sh file" or similar. To overwrite existing files,
- # type "sh file -c".
- # The tool that generated this appeared in the comp.sources.unix newsgroup;
- # send mail to comp-sources-unix@uunet.uu.net if you want that tool.
- # Contents: aux.sh group.c nntp.c
- # Wrapped by storm@texas.dk on Sun May 6 18:19:43 1990
- PATH=/bin:/usr/bin:/usr/ucb ; export PATH
- echo If this archive is complete, you will see the following message:
- echo ' "shar: End of archive 10 (of 22)."'
- if test -f 'aux.sh' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'aux.sh'\"
- else
- echo shar: Extracting \"'aux.sh'\" \(5571 characters\)
- sed "s/^X//" >'aux.sh' <<'END_OF_FILE'
- X
- X# PREFIX is inserted above this line during Make
- X
- Xtrap : 2 3
- X
- XPATH=/bin:$PATH
- Xexport PATH
- X
- X# Paramaters transferred from nn via .param file:
- X# ART_ID Article id to cancel
- X# GROUP Group of article to cancel
- X# WORK Temporary file for response (w initial contents)
- X# FIRST_ACTION First action to perform
- X# EMPTY_CHECK [empty-response-check]
- X# EDITOR [editor]
- X# ED_LINE First line of body in WORK file
- X# SPELL_CHECKER [spell-checker]
- X# PAGER [pager]
- X# APPEND_SIG [append-signature]
- X# QUERY_SIG [query-signature]
- X# NOVICE [expert]
- X# WAIT_PERIOD [response-check-pause]
- X# RECORD [mail/news-record]
- X# MAILER [mailer]
- X# MAILER_PIPE [mailer-pipe-input]
- X# DFLT_ANSW [response-default-answer]
- X
- X. ${HOME}/.nn/.param
- X
- X# first argument is operation to be performed:
- X
- XOPERATION=$1
- X
- X# first we handle 'cancel'
- X
- Xif [ "$OPERATION" = "cancel" ] ; then
- X
- X $INEWS -h << EOF > /tmp/nn$$c 2>&1
- XNewsgroups: $GROUP
- XSubject: cancel $ART_ID
- XControl: cancel $ART_ID
- X
- Xcancel $ART_ID in newsgroup $GROUP
- XEOF
- X
- X x=$?
- X case $x in
- X 0) ;;
- X *) echo '' ; cat /tmp/nn$$c ; sleep 2 ;;
- X esac
- X rm -f /tmp/nn$$c
- X exit $x
- Xfi
- X
- X
- XTRACE=${WORK}T
- XFINAL=${WORK}F
- XCOPY=""
- X
- Xif [ "${FIRST_ACTION}" != "send" ] ; then
- X COPY=${WORK}C
- X cp $WORK $COPY
- Xfi
- X
- X# loop until sent or aborted.
- X
- Xloop=true
- Xprompt=false
- Xpr="Action: a)bort e)dit"
- Xif [ -n "$(SPELL_CHECKER)" ] ; then
- X pr="$pr i)spell"
- Xfi
- Xpr="$pr r)eedit s)end v)iew w)rite: "
- Xif [ -n "${DFLT_ANSW}" ] ; then
- X pr="$pr (${DFLT_ANSW}) "
- Xfi
- X
- Xwhile $loop ; do
- X if [ -n "${FIRST_ACTION}" ] ; then
- X act="${FIRST_ACTION}"
- X FIRST_ACTION=""
- X elif $prompt ; then
- X echo ''
- X ${AWK} 'END{printf "'"${pr}"'"}' < /dev/null
- X read act
- X if [ -z "$act" ] ; then
- X act="${DFLT_ANSW}"
- X fi
- X fi
- X prompt=true
- X
- X case $act in
- X a*)
- X ${AWK} 'END{printf "Confirm abort: (y) "}' < /dev/null
- X read act
- X case "$act" in
- X ""|"y*") rm -f $WORK $COPY ;
- X exit 22 ;;
- X esac
- X ;;
- X e*)
- X # call editor to enter at line $ED_LINE of work file
- X
- X case `basename "${EDITOR-vi}"` in
- X vi|emacs|emacsclient )
- X # Berkeley vi display editor
- X # GNU emacs disply editor
- X ${EDITOR-vi} +${ED_LINE} $WORK
- X ;;
- X ded )
- X # QMC ded display editor
- X $EDITOR -l${ED_LINE} $WORK
- X ;;
- X uemacs )
- X # micro emacs
- X $EDITOR -g${ED_LINE} $WORK
- X ;;
- X * )
- X # Unknown editor
- X $EDITOR $WORK
- X ;;
- X esac
- X
- X if [ ! -s $WORK ] ; then
- X rm -f $WORK $COPY
- X exit 22
- X fi
- X
- X if ${EMPTY_CHECK} ; then
- X if cmp -s $WORK $COPY ; then
- X rm -f $WORK $COPY
- X exit 22
- X fi
- X fi
- X ;;
- X
- X i*)
- X if [ -n "${SPELL_CHECKER}" ] ; then
- X ${SPELL_CHECKER} ${WORK}
- X else
- X echo "spell-checker not defined"
- X fi
- X ;;
- X
- X r*)
- X ${AWK} 'END{printf "Edit original file: (y) "}' < /dev/null
- X read act
- X case "$act" in
- X ""|"y*") cp $COPY $WORK ;;
- X esac
- X FIRST_ACTION=edit
- X ;;
- X
- X s*)
- X loop=false
- X ;;
- X
- X v*)
- X ${PAGER-cat} $WORK
- X ;;
- X
- X w*)
- X echo "Append article to file:"
- X read FNAME
- X if [ -n "$FNAME" ] ; then
- X { cat $WORK ; echo ; } >> $FNAME
- X fi
- X ;;
- X esac
- Xdone
- X
- Xcase "$OPERATION" in
- X
- X reply|forward|mail)
- X if [ ${APPEND_SIG} = "true" -a -f $HOME/.signature ] ; then
- X if ${QUERY_SIG} ; then
- X ${AWK} 'END{printf "Append .signature? (y) "}' < /dev/null
- X read ans
- X else
- X ans=y
- X fi
- X case $ans in
- X ''|y*|Y*)
- X echo "--" >> $WORK
- X cat $HOME/.signature >> $WORK
- X ;;
- X esac
- X fi
- X ;;
- X follow|post)
- X if ${NOVICE} ; then
- X echo "Be patient! Your new article will not show up immediately."
- X case ${WAIT_PERIOD-0} in
- X 0|1) WAIT_PERIOD=2 ;;
- X esac
- X fi
- X ;;
- Xesac
- X
- X{
- X trap 'echo SIGNAL' 1 2 3
- X
- X grep -v "^Orig-To: " $WORK > $FINAL
- X
- X LOGNAME="${LOGNAME-$USER}"
- X if [ -z "${LOGNAME}" ] ; then
- X set `who am i`
- X LOGNAME="$1"
- X fi
- X
- X if [ -n "${RECORD}" ] ; then
- X {
- X # keep a copy of message in $RECORD (in mail format)
- X set `date`
- X if [ $3 -gt 9 ] ; then
- X echo From ${LOGNAME} $1 $2 $3 $4 $6 $7
- X else
- X echo From ${LOGNAME} $1 $2 " $3" $4 $6 $7
- X fi
- X echo "From: ${LOGNAME}"
- X cat $FINAL
- X echo ''
- X } >> "$RECORD"
- X fi
- X
- X case "$OPERATION" in
- X
- X reply|forward|mail)
- X if ${MAILER_PIPE} ; then
- X $MAILER < $FINAL
- X x=$?
- X else
- X $MAILER $FINAL
- X x=$?
- X fi
- X case $x in
- X 0) ;;
- X *) echo $MAILER failed ;;
- X esac
- X ;;
- X
- X follow|post)
- X $INEWS -h < $FINAL
- X case $? in
- X 0) sleep 60 ;;
- X *) echo $INEWS failed ;;
- X esac
- X ;;
- X
- X *)
- X echo "Invalid operation: $OPERATION -- help"
- X OPERATION="nn response operation"
- X ;;
- X esac > $TRACE 2>&1
- X
- X if [ -s $TRACE ] ; then
- X if [ -s $HOME/dead.letter ] ; then
- X cat $HOME/dead.letter >> $HOME/dead.letters
- X echo '' >> $HOME/dead.letters
- X fi
- X cat $WORK > $HOME/dead.letter
- X
- X # Gripe: Error-report is lost if REC_MAIL was the problem
- X {
- X echo "To: ${LOGNAME}"
- X echo "Subject: $OPERATION failed"
- X echo ''
- X cat $TRACE
- X echo ''
- X echo 'Your response has been saved in ~/dead.letter'
- X echo ''
- X echo 'Your article/letter follows:'
- X cat $WORK
- X } > $FINAL
- X if ${MAILER_PIPE} ; then
- X $MAILER < $FINAL
- X else
- X $MAILER $FINAL
- X fi
- X else
- X # keep TRACE file a little while for test at end of script
- X sleep 3
- X fi
- X rm -f $WORK $COPY $TRACE $FINAL
- X
- X} > /dev/null 2>&1 &
- X
- Xcase ${WAIT_PERIOD-0} in
- X 0) ;;
- X *) sleep ${WAIT_PERIOD} ;;
- Xesac
- X
- Xif [ -s "$TRACE" ] ; then
- X exit 1
- Xfi
- X
- Xexit 0
- END_OF_FILE
- if test 5571 -ne `wc -c <'aux.sh'`; then
- echo shar: \"'aux.sh'\" unpacked with wrong size!
- fi
- # end of 'aux.sh'
- fi
- if test -f 'group.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'group.c'\"
- else
- echo shar: Extracting \"'group.c'\" \(22393 characters\)
- sed "s/^X//" >'group.c' <<'END_OF_FILE'
- X/*
- X * (c) Copyright 1990, Kim Fabricius Storm. All rights reserved.
- X *
- X * News group access.
- X */
- X
- X#include "config.h"
- X#include "articles.h"
- X#include "db.h"
- X#include "term.h"
- X#include "menu.h"
- X#include "keymap.h"
- X#ifdef HAVE_SYSLOG
- X#include <syslog.h>
- X#endif
- X
- Xexport int dont_split_digests = 0;
- Xexport int dont_sort_articles = 0;
- Xexport int also_cross_postings = 0;
- Xexport int also_unsub_groups = 0;
- Xexport int entry_message_limit = 400;
- X
- Ximport int article_limit, also_read_articles;
- Ximport int no_update, novice, case_fold_search;
- Ximport int merged_menu, keep_unsubscribed, keep_unsub_long;
- Ximport int killed_articles;
- Ximport int seq_cross_filtering;
- Ximport char *default_save_file;
- X
- Ximport char delayed_msg[];
- Ximport int32 db_read_counter;
- X
- X/*
- X * completion of group name
- X */
- X
- Xgroup_completion(hbuf, ix)
- Xchar *hbuf;
- Xint ix;
- X{
- X static group_number next_group, n1, n2;
- X static char *head, *tail, *last;
- X static int tail_offset, prev_lgt, l1, l2;
- X static group_header *prev_group, *p1, *p2;
- X register group_header *gh;
- X register char *t1, *t2;
- X int order;
- X
- X if (ix < 0) return 0;
- X
- X if (hbuf) {
- X n2 = next_group = 0;
- X p2 = prev_group = NULL;
- X l2 = 0;
- X
- X if (head = strrchr(hbuf, ','))
- X head++;
- X else
- X head = hbuf;
- X tail = hbuf + ix;
- X tail_offset = ix - (head - hbuf);
- X if (last = strrchr(head, '.')) last++; else last = head;
- X return 1;
- X }
- X
- X if (ix) {
- X n1 = next_group, p1 = prev_group, l1 = prev_lgt;
- X next_group = n2, prev_group = p2, prev_lgt = l2;
- X list_completion((char *)NULL);
- X }
- X
- X *tail = NUL;
- X
- X while (next_group < master.number_of_groups) {
- X gh = sorted_groups[next_group++];
- X if (gh->master_flag & M_IGNORE_GROUP) continue;
- X if (gh->group_name_length <= tail_offset) continue;
- X
- X if (prev_group &&
- X strncmp(prev_group->group_name, gh->group_name, prev_lgt) == 0)
- X continue;
- X
- X order = strncmp(gh->group_name, head, tail_offset);
- X if (order < 0) continue;
- X if (order > 0) break;
- X
- X t1 = gh->group_name + tail_offset;
- X if (t2 = strchr(t1, '.')) {
- X strncpy(tail, t1, t2 - t1 + 1);
- X tail[t2 - t1 + 1] = NUL;
- X } else
- X strcpy(tail, t1);
- X
- X prev_group = gh;
- X prev_lgt = tail_offset + strlen(tail);
- X if (ix) {
- X if (list_completion(last) == 0) break;
- X } else
- X return 1;
- X }
- X
- X if (ix) {
- X n2 = next_group, p2 = prev_group, l2 = prev_lgt;
- X if (n2 > master.number_of_groups)
- X n2 = 0, p2 = NULL, l2 = 0;
- X next_group = n1, prev_group = p1, prev_lgt = l1;
- X return 1;
- X }
- X
- X next_group = 0;
- X prev_group = NULL;
- X return 0;
- X}
- X
- Xstatic int group_level = 0;
- X
- Xstatic print_header()
- X{
- X extern long unread_articles;
- X extern int unread_groups;
- X
- X so_printxy(0, 0, "Newsgroup: %s", current_group->group_name);
- X
- X if (current_group->group_flag & G_MERGED) printf(" MERGED");
- X
- X clrline();
- X
- X so_gotoxy(-1, 0, 0);
- X
- X so_printf("Articles: %d", n_articles);
- X
- X if (no_update) {
- X so_printf(" NO UPDATE");
- X } else {
- X if (current_group->current_first > current_group->first_article + 1)
- X so_printf((killed_articles > 0) ? "(L-%ld,K-%d)" : "(L-%ld)",
- X current_group->current_first - current_group->first_article - 1,
- X killed_articles);
- X else
- X if (killed_articles > 0)
- X so_printf(" (K-%d)", killed_articles);
- X
- X if (unread_articles > 0) {
- X so_printf(" of %ld", unread_articles);
- X if (unread_groups > 0)
- X so_printf("/%d", unread_groups);
- X }
- X
- X if (current_group->group_flag & G_UNSUBSCRIBED)
- X so_printf(" UNSUB");
- X else if (current_group->group_flag & G_NEW)
- X so_printf(" NEW");
- X
- X if (current_group->unread_count <= 0)
- X so_printf(" READ");
- X
- X if (group_level > 1)
- X so_printf(" *NO*UPDATE*");
- X }
- X
- X so_end();
- X
- X return 1; /* number of lines in header */
- X}
- X
- X/*
- X * interpretation of first_art:
- X * >0 Read articles first_art..last_db_article (also read)
- X * -1 Read unread or read articles accoring to -x and -aN flags
- X */
- X
- Xgroup_menu(gh, first_art, access_mode, mask, menu)
- Xregister group_header *gh;
- Xarticle_number first_art;
- Xregister flag_type access_mode;
- Xchar *mask;
- Xfct_type menu;
- X{
- X int status, was_unread, unread_at_reentry = 0;
- X int menu_cmd, o_killed;
- X article_number prev_last, n;
- X register group_header *mg_head;
- X
- X#define menu_return(cmd) { menu_cmd = (cmd); goto menu_exit; }
- X
- X o_killed = killed_articles;
- X mark_var_stack();
- X
- X mg_head = (group_level == 0 && gh->group_flag & G_MERGE_HEAD) ? gh : NULL;
- X group_level++;
- X
- X reenter:
- X for (; gh; gh = gh->merge_with) {
- X if (init_group(gh) <= 0) {
- X if (mg_head != NULL) continue;
- X menu_return( ME_NEXT );
- X }
- X
- X if (unread_at_reentry) restore_unread(gh);
- X
- X m_invoke(-1);
- X
- X killed_articles = 0;
- X
- X /* don't lose (a few) new articles when reentering a read group */
- X /* (the user will expect the menu to be the same, and may overlook */
- X /* the new articles) */
- X if (gh->group_flag & G_READ) goto update_unsafe;
- X
- X#ifdef RENUMBER_DANGER
- X prev_last = gh->last_db_article;
- X#endif
- X was_unread = add_unread(gh, -1);
- X
- X switch (update_group(gh)) {
- X case -1:
- X status = -1;
- X goto access_exception;
- X case -3:
- X return ME_QUIT;
- X }
- X
- X#ifdef RENUMBER_DANGER
- X if (gh->last_db_article < prev_last) {
- X /* expire + renumbering */
- X flag_type updflag;
- X
- X if ((gh->last_article = gh->first_db_article - 1) < 0)
- X gh->last_article = 0;
- X gh->first_article = gh->last_article;
- X updflag = gh->group_flag & G_READ;
- X gh->group_flag &= ~G_READ;
- X update_rc_all(gh, 0);
- X gh->group_flag &= ~G_READ;
- X gh->group_flag |= updflag;
- X }
- X#endif
- X
- X if (was_unread)
- X add_unread(gh, 1);
- X
- X update_unsafe:
- X if ((gh->current_first = first_art) < 0) {
- X if (access_mode & ACC_ORIG_NEWSRC)
- X gh->current_first = gh->first_article + 1;
- X else if (access_mode & ACC_ALSO_READ_ARTICLES)
- X gh->current_first = gh->first_db_article;
- X else
- X gh->current_first = gh->last_article + 1;
- X }
- X
- X if (gh->current_first <= 0) gh->current_first = 1;
- X
- X if (article_limit > 0) {
- X n = gh->last_db_article - article_limit + 1;
- X if (gh->current_first < n) gh->current_first = n;
- X }
- X
- X if (mg_head != NULL) {
- X access_mode |= ACC_MERGED_MENU | ACC_DONT_SORT_ARTICLES;
- X if (mg_head != gh) access_mode |= ACC_EXTRA_ARTICLES;
- X }
- X
- X if (entry_message_limit &&
- X (n = gh->last_db_article - gh->current_first) >= entry_message_limit) {
- X clrdisp();
- X printf("Reading %s: %ld articles...", gh->group_name, (long)n);
- X } else
- X home();
- X fl;
- X
- X status = access_group(gh, gh->current_first, gh->last_db_article,
- X access_mode, mask);
- X
- X access_exception:
- X
- X if (status < 0) {
- X if (status == -2) {
- X clrdisp();
- X printf("Read error on group %s (NFS timeout?)\n\r",
- X current_group->group_name);
- X printf("Skipping to next group.... (interrupt to quit)\n\r");
- X if (any_key(0) == K_interrupt) nn_exit(0);
- X }
- X
- X if (status <= -10) {
- X clrdisp();
- X msg("DATABASE CORRUPTED FOR GROUP %s", gh->group_name);
- X#ifdef HAVE_SYSLOG
- X openlog("nn", LOG_CONS, LOG_DAEMON);
- X syslog(LOG_ALERT, "database corrupted for newsgroup %s.",
- X gh->group_name);
- X closelog();
- X#endif
- X user_delay(5);
- X }
- X if (mg_head != NULL) continue;
- X menu_return( ME_NEXT );
- X }
- X
- X if (mg_head == NULL) break;
- X }
- X
- X if (n_articles == 0) {
- X m_break_entry();
- X menu_return( ME_NO_ARTICLES );
- X }
- X
- X if (mg_head != NULL) {
- X if (!dont_sort_articles) sort_articles(-1);
- X init_group(mg_head);
- X }
- X
- X samemenu:
- X menu_cmd = CALL(menu)(print_header);
- X
- X if (menu_cmd == ME_REENTER_GROUP) {
- X if (group_level != 1) {
- X strcpy(delayed_msg, "can only unread groups at topmost level");
- X goto samemenu;
- X }
- X free_memory();
- X if (mg_head) gh = mg_head;
- X unread_at_reentry = 1;
- X goto reenter;
- X }
- X
- X menu_exit:
- X
- X n = 1; /* must unsort articles */
- X if (mg_head != NULL) gh = mg_head;
- X
- X if (!no_update) {
- X do {
- X if (mask == NULL && (access_mode & ACC_ALSO_READ_ARTICLES) == 0
- X && first_art < 0) {
- X if (menu_cmd != ME_NO_ARTICLES) gh->group_flag &= ~G_NEW;
- X if ((gh->group_flag & G_UNSUBSCRIBED) == 0 || keep_unsub_long) {
- X if (n) sort_articles(0);
- X n = 0;
- X update_rc(gh);
- X }
- X }
- X gh->current_first = 0;
- X } while (mg_head != NULL && (gh = gh->merge_with) != NULL);
- X }
- X
- X killed_articles = o_killed;
- X restore_variables();
- X group_level--;
- X
- X return menu_cmd;
- X}
- X
- X
- Xgoto_group(command, ah, access_mode)
- Xint command;
- Xarticle_header *ah;
- Xflag_type access_mode;
- X{
- X register group_header *gh;
- X char ans1, *answer, *mask, buffer[FILENAME], fbuffer[FILENAME];
- X article_number first;
- X memory_marker mem_marker;
- X group_header *orig_group;
- X int menu_cmd;
- X extern int menu(), file_completion();
- X extern article_header *get_menu_article();
- X extern int get_from_macro;
- X extern group_header *jump_to_group;
- X
- X#define goto_return( cmd ) \
- X { menu_cmd = cmd; goto goto_exit; }
- X
- X m_startinput();
- X
- X gh = orig_group = current_group;
- X
- X mask = NULL;
- X
- X if (command == K_GOTO_GROUP)
- X goto get_group_name;
- X
- X if (command == K_ADVANCE_GROUP)
- X gh = gh->next_group;
- X else
- X gh = gh->prev_group;
- X
- X for (;;) {
- X if (gh == NULL)
- X goto_return(ME_NO_REDRAW);
- X
- X if (gh->first_db_article < gh->last_db_article && gh->current_first <= 0) {
- X sprintf(buffer, "%s%s%s) ",
- X (gh->group_flag & G_UNSUBSCRIBED) ? " UNSUB" : "",
- X (gh->group_flag & G_MERGE_HEAD) ? " MERGED" : "",
- X gh->unread_count <= 0 ? " READ" : "" );
- X buffer[0] = buffer[0] == ')' ? NUL : '(';
- X
- X prompt("\1Enter\1 %s %s? (ABGNPy) ", gh->group_name, buffer);
- X
- X command = get_c();
- X if (command & GETC_COMMAND) goto_return(ME_REDRAW);
- X if (command == 'y') break;
- X command = menu_key_map[command];
- X }
- X
- X switch (command) {
- X case K_CONTINUE:
- X case K_CONTINUE_NO_MARK:
- X break;
- X
- X case K_ADVANCE_GROUP:
- X gh = gh->next_group;
- X continue;
- X
- X case K_BACK_GROUP:
- X gh = gh->prev_group;
- X continue;
- X
- X case K_GOTO_GROUP:
- X goto get_group_name;
- X
- X case K_PREVIOUS:
- X while (gh = gh->prev_group) {
- X if (gh->group_flag & G_MERGE_SUB) continue;
- X if (gh->group_flag & G_COUNTED) break;
- X if (gh->newsrc_line != gh->newsrc_orig) break;
- X }
- X continue;
- X
- X case K_NEXT_GROUP_NO_UPDATE:
- X while (gh = gh->next_group) {
- X if (gh->group_flag & G_MERGE_SUB) continue;
- X if (gh->group_flag & G_COUNTED) break;
- X if (gh->newsrc_line != gh->newsrc_orig) break;
- X }
- X continue;
- X
- X default:
- X goto_return(ME_NO_REDRAW);
- X }
- X break;
- X }
- X
- X if (gh == orig_group) goto_return(ME_NO_REDRAW);
- X
- X goto get_first;
- X
- X get_group_name:
- X
- X if (current_group == NULL) {
- X prompt("\1Enter Group or Folder\1 (+./~) ");
- X answer = get_s(NONE, NONE, "+./~", group_completion);
- X } else {
- X prompt("\1Group or Folder\1 (+./~ %%%s=sneN) ",
- X (gh->master_flag & M_AUTO_ARCHIVE) ? "@" : "");
- X strcpy(buffer, "++./0123456789~=% ");
- X if (gh->master_flag & M_AUTO_ARCHIVE) buffer[0] = '@';
- X answer = get_s(NONE, NONE, buffer, group_completion);
- X }
- X
- X if (answer == NULL) goto_return(ME_NO_REDRAW);
- X
- X if ((ans1 = *answer) == NUL || ans1 == SP) {
- X if (current_group == NULL) goto_return(ME_NO_REDRAW);
- X goto get_first;
- X }
- X
- X sprintf(buffer, "%c", ans1);
- X
- X switch (ans1) {
- X
- X case '@':
- X if (current_group == NULL ||
- X (current_group->master_flag & M_AUTO_ARCHIVE) == 0)
- X goto_return(ME_NO_REDRAW);
- X answer = current_group->archive_file;
- X goto get_folder;
- X
- X case '%':
- X if (current_group == NULL) goto_return(ME_NO_REDRAW);
- X if ((current_group->group_flag & G_FOLDER) == 0) {
- X if (!ah) {
- X prompt("\1READ\1");
- X if ((ah = get_menu_article()) == NULL)
- X goto_return(ME_NO_REDRAW);
- X }
- X if ((ah->flag & A_DIGEST) == 0) {
- X *group_file_name = NUL;
- X sprintf(fbuffer, "%s%ld", group_path_name, ah->a_number);
- X answer = fbuffer;
- X goto get_folder;
- X }
- X }
- X
- X msg("cannot split articles inside a folder or digest");
- X goto_return(ME_NO_REDRAW);
- X
- X case '.':
- X case '~':
- X strcat(buffer, "/");
- X case '+':
- X case '/':
- X if (!get_from_macro) {
- X prompt("\1Folder\1 ");
- X answer = get_s(NONE, buffer, NONE, file_completion);
- X if (answer == NULL || answer[0] == NUL) goto_return(ME_NO_REDRAW);
- X }
- X goto get_folder;
- X
- X case 'a':
- X if (answer[1] != NUL && strcmp(answer, "all")) break;
- X if (current_group == NULL) goto_return(ME_NO_REDRAW);
- X access_mode |= ACC_ALSO_READ_ARTICLES | ACC_ALSO_CROSS_POSTINGS;
- X first = 0;
- X goto more_articles;
- X
- X case 'u':
- X if (answer[1] != NUL && strcmp(answer, "unread")) break;
- X if (current_group == NULL) goto_return(ME_NO_REDRAW);
- X access_mode |= ACC_ORIG_NEWSRC;
- X first = gh->first_article + 1;
- X goto enter_new_level;
- X
- X case 'e':
- X case 'n':
- X case 's':
- X if (answer[1] != NUL && answer[1] != '=') break;
- X /* fall thru */
- X case '=':
- X if (current_group == NULL) goto_return(ME_NO_REDRAW);
- X goto get_mask;
- X
- X case '0':
- X case '1':
- X case '2':
- X case '3':
- X case '4':
- X case '5':
- X case '6':
- X case '7':
- X case '8':
- X case '9':
- X if (current_group == NULL) goto_return(ME_NO_REDRAW);
- X if (gh->current_first <= gh->first_db_article) {
- X msg("No extra articles");
- X flush_input();
- X goto_return(ME_NO_REDRAW);
- X }
- X if (!get_from_macro) {
- X prompt("\1Number of extra articles\1 max %ld: ",
- X gh->current_first - gh->first_db_article);
- X sprintf(buffer, "%c", ans1);
- X answer = get_s(NONE, buffer, NONE, NULL_FCT);
- X if (answer == NULL || *answer == NUL) goto_return(ME_NO_REDRAW);
- X }
- X first = gh->current_first - atol(answer);
- X goto more_articles;
- X
- X default:
- X break;
- X }
- X
- X if ((gh = lookup(answer)) == NULL) {
- X msg("No group named %s", answer);
- X goto_return(ME_NO_REDRAW);
- X }
- X
- X
- X get_first:
- X
- X m_advinput();
- X
- X if (gh->master_flag & M_BLOCKED ||
- X gh->last_db_article == 0 ||
- X gh->last_db_article < gh->first_db_article) {
- X msg("Group %s is %s", gh->group_name,
- X (gh->master_flag & M_BLOCKED) ? "blocked" : "empty");
- X goto_return(ME_NO_REDRAW);
- X }
- X
- X if (gh != orig_group) {
- X if (gh->current_first > 0) {
- X msg("Group %s already active", gh->group_name);
- X goto_return(ME_NO_REDRAW);
- X }
- X gh->current_first = gh->last_db_article + 1;
- X }
- X
- X ans1 = ah ? 's' : 'a';
- X if (gh != orig_group) {
- X if (gh->unread_count > 0) ans1 = 'j';
- X } else {
- X if (gh->unread_count > 0 && gh->current_first > (gh->first_article + 1))
- X ans1 = 'u';
- X else if (gh->current_first <= gh->first_db_article)
- X ans1 = 's'; /* no more articles to read */
- X }
- X
- X prompt("\1Number of%s articles\1 (%sua%ssne) (%c) ",
- X gh == orig_group ? " extra" : "",
- X (gh->unread_count > 0) ? "j" : "",
- X (gh->master_flag & M_AUTO_ARCHIVE) ? "@" : "",
- X ans1);
- X
- X if (novice)
- X msg("Use: j)ump u)nread a)ll @)archive s)ubject n)ame e)ither or number");
- X
- X answer = get_s(NONE, NONE, " jua@s=ne", NULL_FCT);
- X if (answer == NULL) goto_return(ME_NO_REDRAW);
- X if (*answer == NUL || *answer == SP) answer = &ans1;
- X
- X switch (*answer) {
- X case '@':
- X if ((gh->master_flag & M_AUTO_ARCHIVE) == 0) {
- X msg("No archive");
- X goto_return(ME_NO_REDRAW);
- X }
- X answer = gh->archive_file;
- X goto get_folder;
- X
- X case 'u':
- X access_mode |= ACC_ORIG_NEWSRC;
- X first = gh->first_article + 1;
- X goto enter_new_level;
- X
- X case 'j':
- X if (gh == orig_group || gh->unread_count <= 0) {
- X msg("Cannot jump - no unread articles");
- X goto_return(ME_NO_REDRAW);
- X }
- X jump_to_group = gh;
- X goto_return(ME_QUIT);
- X
- X case 'a':
- X first = 0;
- X access_mode |= ACC_ALSO_READ_ARTICLES | ACC_ALSO_CROSS_POSTINGS;
- X goto more_articles;
- X
- X case '=':
- X case 's':
- X case 'n':
- X case 'e':
- X goto get_mask;
- X
- X case '0':
- X case '1':
- X case '2':
- X case '3':
- X case '4':
- X case '5':
- X case '6':
- X case '7':
- X case '8':
- X case '9':
- X first = gh->current_first - atol(answer);
- X goto more_articles;
- X
- X default:
- X ding();
- X goto_return(ME_NO_REDRAW);
- X }
- X
- X
- X more_articles:
- X if (first > gh->last_db_article) goto_return(ME_NO_REDRAW);
- X
- X if (gh != orig_group) goto enter_new_level;
- X
- X if (gh->current_first <= gh->first_db_article) {
- X msg("No extra articles");
- X goto_return(ME_NO_REDRAW);
- X }
- X
- X if (access_group(gh, first, gh->current_first == gh->first_article + 1 ?
- X gh->last_db_article : gh->current_first - 1,
- X ACC_EXTRA_ARTICLES | ACC_ALSO_CROSS_POSTINGS |
- X ACC_ALSO_READ_ARTICLES | ACC_ONLY_READ_ARTICLES,
- X (char *)NULL) < 0) {
- X msg("Cannot read extra articles (now)");
- X goto_return(ME_NO_REDRAW);
- X }
- X
- X gh->current_first = first;
- X goto_return(ME_REDRAW);
- X
- X get_folder:
- X m_endinput();
- X if (strcmp(answer, "+") == 0)
- X answer = (gh->save_file != NULL) ? gh->save_file : default_save_file;
- X if (!expand_file_name(buffer, answer, 1)) goto_return (ME_NO_REDRAW);
- X menu_cmd = folder_menu(buffer);
- X gh = NULL;
- X goto goto_exit;
- X
- X get_mask:
- X first = 0;
- X access_mode |= ACC_ALSO_READ_ARTICLES | ACC_ALSO_CROSS_POSTINGS;
- X switch (*answer) {
- X case '=':
- X *answer = 's';
- X case 's':
- X access_mode |= ACC_ON_SUBJECT;
- X break;
- X case 'n':
- X access_mode |= ACC_ON_SENDER;
- X break;
- X case 'e':
- X access_mode |= ACC_ON_SUBJECT | ACC_ON_SENDER;
- X break;
- X }
- X
- X if (get_from_macro || answer[1] == '=') {
- X mask = answer + 1;
- X if (*mask == '=') mask++;
- X } else {
- X prompt("%c=", *answer);
- X mask = get_s(ah == NULL ? NONE :
- X (access_mode & ACC_ON_SUBJECT ? ah->subject : ah->sender),
- X NONE, ah ? NONE : "%=", NULL_FCT);
- X if (mask == NULL) goto_return(ME_NO_REDRAW);
- X if (*mask == '%' || *mask == '=') {
- X if ((ah = get_menu_article()) == 0) goto_return(ME_NO_REDRAW);
- X *mask = NUL;
- X }
- X }
- X
- X if (*mask == NUL) {
- X if (ah == NULL) goto_return(ME_NO_REDRAW);
- X strncpy(mask, (access_mode & ACC_ON_SUBJECT) ? ah->subject : ah->sender, GET_S_BUFFER);
- X mask[GET_S_BUFFER-1] = NUL;
- X }
- X
- X if (*mask) {
- X if (case_fold_search) fold_string(mask);
- X } else
- X mask = NULL;
- X
- X enter_new_level:
- X mark_memory(&mem_marker);
- X m_endinput();
- X menu_cmd = group_menu(gh, first, access_mode, mask, menu);
- X release_memory(&mem_marker);
- X
- Xgoto_exit:
- X if (gh != orig_group)
- X if (orig_group) init_group(orig_group);
- X
- X m_endinput();
- X return menu_cmd;
- X}
- X
- Xstatic merged_header()
- X{
- X so_printxy(0, 0, "MERGED NEWS GROUPS: %d ARTICLES", n_articles);
- X clrline();
- X
- X return 1;
- X}
- X
- Xmerge_and_read(access_mode, mask)
- Xflag_type access_mode;
- Xchar *mask;
- X{
- X register group_header *gh;
- X group_header dummy_group;
- X time_t t1, t2;
- X long kb = 0, kbleft = 0;
- X
- X time(&t1); t2 = 0;
- X
- X free_memory();
- X
- X access_mode |= ACC_DONT_SORT_ARTICLES | ACC_MERGED_MENU;
- X if (!seq_cross_filtering)
- X if (also_read_articles || mask || also_cross_postings)
- X access_mode |= ACC_ALSO_CROSS_POSTINGS;
- X if (dont_split_digests)
- X access_mode |= ACC_DONT_SPLIT_DIGESTS;
- X
- X Loop_Groups_Sequence(gh) {
- X if (gh->group_flag & G_FOLDER) continue;
- X if (gh->master_flag & M_NO_DIRECTORY) continue;
- X if ((gh->group_flag & G_UNSUBSCRIBED) && !also_unsub_groups)
- X continue;
- X if (!also_read_articles && gh->last_article >= gh->last_db_article)
- X continue;
- X
- X kbleft += gh->data_write_offset;
- X }
- X
- X Loop_Groups_Sequence(gh) {
- X if (s_hangup || s_keyboard) break;
- X
- X if (gh->group_flag & G_FOLDER) {
- X printf("\n\rIgnoring folder: %s\n\r", gh->group_name);
- X continue;
- X }
- X
- X if (gh->master_flag & M_NO_DIRECTORY) continue;
- X if ((gh->group_flag & G_UNSUBSCRIBED) && !also_unsub_groups)
- X continue;
- X
- X if (also_read_articles)
- X access_mode |= ACC_ALSO_READ_ARTICLES;
- X else
- X if (gh->last_article >= gh->last_db_article)
- X continue;
- X
- X if (init_group(gh) <= 0) continue;
- X
- X if (t2 > 2 && kb > 50000) {
- X printf("\r%4lds\t%lds\t%s", kbleft/(kb/t2), (long)t2, gh->group_name);
- X } else
- X#ifdef KBYTE_PER_SECOND
- X printf("\r%4lds\t%lds\t%s", kbleft/(KBYTE_PER_SECOND*1024)),
- X (long)t2, gh->group_name);
- X#else
- X printf("\r\t%lds\t%s", (long)t2, gh->group_name);
- X#endif
- X clrline();
- X
- X access_group(gh, (article_number)(-1), gh->last_db_article,
- X access_mode, mask);
- X
- X time(&t2); t2 -= t1;
- X kb += gh->data_write_offset;
- X kbleft -= gh->data_write_offset;
- X }
- X merge_memory();
- X if (n_articles == 0) return;
- X if (!dont_sort_articles) sort_articles(-1);
- X
- X dummy_group.group_flag = G_FAKED;
- X dummy_group.master_flag = 0;
- X dummy_group.save_file = NULL;
- X dummy_group.group_name = "dummy";
- X dummy_group.kill_list = NULL;
- X
- X current_group = &dummy_group;
- X
- X kb = (kb + 1023) >> 10;
- X sprintf(delayed_msg, "Read %ld articles in %ld seconds (%ld kbyte/s)",
- X (long)db_read_counter, (long)t2, t2 > 0 ? kb/t2 : kb);
- X
- X menu(merged_header);
- X
- X free_memory();
- X}
- X
- Xunsubscribe(gh)
- Xgroup_header *gh;
- X{
- X if (no_update) {
- X msg("nn started in \"no update\" mode");
- X return 0;
- X }
- X
- X if (gh->group_flag & G_FOLDER) {
- X msg("cannot unsubscribe to a folder");
- X return 0;
- X }
- X
- X if (gh->group_flag & G_UNSUBSCRIBED) {
- X prompt("Already unsubscribed. \1Resubscribe to\1 %s ? ",
- X gh->group_name);
- X if (yes(0) <= 0) return 0;
- X
- X add_to_newsrc(gh);
- X add_unread(gh, 1);
- X } else {
- X prompt("\1Unsubscribe to\1 %s ? ", gh->group_name);
- X if (yes(0) <= 0) return 0;
- X
- X add_unread(gh, -1);
- X update_rc_all(gh, 1);
- X }
- X
- X return 1;
- X}
- X
- Xstatic disp_group(gh)
- Xgroup_header *gh;
- X{
- X if (pg_next() < 0) return -1;
- X
- X printf("%c%6ld%c%s%s%s",
- X (gh->group_flag & G_MERGED) == 0 ? ' ' :
- X (gh->group_flag & G_MERGE_HEAD) ? '&' : '+',
- X
- X (long)(gh->unread_count),
- X
- X (gh == current_group) ? '*' : ' ',
- X
- X gh->group_name,
- X
- X (gh->group_flag & G_NEW) ? " NEW" :
- X (gh->group_flag & G_UNSUBSCRIBED) ? " (!)" : "",
- X
- X gh->enter_macro ? " %" : "");
- X
- X return 0;
- X}
- X
- X/*
- X * amount interpretation:
- X * -1 presentation sequence, unread,subscribed+current
- X * 0 unread,subscribed
- X * 1 unread,all
- X * 2 all,all 3=>unsub
- X */
- X
- Xgroup_overview(amount)
- Xint amount;
- X{
- X register group_header *gh;
- X
- X clrdisp();
- X
- X pg_init(0, 2);
- X
- X if (amount < 0) {
- X Loop_Groups_Sequence(gh) {
- X if (gh->group_flag & G_FAKED) continue;
- X if (gh->master_flag & M_NO_DIRECTORY) continue;
- X if (gh != current_group)
- X if ((gh->group_flag & G_COUNTED) == 0) continue;
- X if (disp_group(gh) < 0) break;
- X }
- X } else
- X Loop_Groups_Sorted(gh) {
- X if (gh->master_flag & (M_NO_DIRECTORY | M_IGNORE_GROUP)) continue;
- X if (amount <= 1 && gh->unread_count <= 0) continue;
- X if (amount == 0 && (gh->group_flag & G_UNSUBSCRIBED)) continue;
- X if (amount == 3 && (gh->group_flag & G_UNSUBSCRIBED) == 0) continue;
- X
- X if (disp_group(gh) < 0) break;
- X }
- X
- X pg_end();
- X}
- END_OF_FILE
- if test 22393 -ne `wc -c <'group.c'`; then
- echo shar: \"'group.c'\" unpacked with wrong size!
- fi
- # end of 'group.c'
- fi
- if test -f 'nntp.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'nntp.c'\"
- else
- echo shar: Extracting \"'nntp.c'\" \(22676 characters\)
- sed "s/^X//" >'nntp.c' <<'END_OF_FILE'
- X/*
- X * nntp module for nn.
- X *
- X * The original taken from the nntp 1.5 clientlib.c
- X * Modified heavily for nn.
- X *
- X * Rene' Seindal (seindal@diku.dk) Thu Dec 1 18:41:23 1988
- X *
- X * I have modified Rene's code quite a lot for 6.4 -- I hope he
- X * can still recognize a bit here and a byte there; in any case,
- X * any mistakes are mine :-) ++Kim
- X */
- X
- X
- X#include "config.h"
- X
- X/*
- X * nn maintains a cache of recently used articles to improve efficiency.
- X * To change the size of the cache, define NNTPCACHE in config.h to be
- X * the new size of this cache.
- X */
- X
- X#ifndef NNTPCACHE
- X#define NNTPCACHE 10
- X#endif
- X
- X#ifdef NNTP
- X#include <stdio.h>
- X#include "nntp.h"
- X#include <sys/socket.h>
- X#include <netdb.h>
- X
- X/* This is necessary due to the definitions in m-XXX.h */
- X#if !defined(NETWORK_DATABASE) || defined(NETWORK_BYTE_ORDER)
- X#include <netinet/in.h>
- X#endif
- X
- Ximport char *db_directory, *tmp_directory, *news_active;
- X
- Xexport char nntp_server[256]; /* name of nntp server */
- Xexport int use_nntp = 0; /* bool: t iff we use nntp */
- Xexport int nntp_failed = 0; /* bool: t iff connection is broken in
- X nntp_get_article() or nntp_get_active() */
- X
- Xexport nntp_cache_size = NNTPCACHE;
- Xexport char *nntp_cache_dir = NULL;
- X
- Xexport int nntp_local_server = 0;
- Xexport int nntp_debug = 0;
- X
- Ximport int silent, no_update;
- X
- Ximport int errno, sys_nerr;
- Ximport char *sys_errlist[];
- Xextern int user_error();
- Xextern int sys_error();
- X
- X#define syserr() (errno >= 0 && errno < sys_nerr ? \
- X sys_errlist[errno] : "Unknown error.")
- X
- Ximport char *mktemp();
- X
- Xstatic FILE *nntp_in = NULL; /* fp for reading from server */
- Xstatic FILE *nntp_out = NULL; /* fp for writing to server */
- Xstatic int is_connected = 0; /* bool: t iff we are connected */
- Xstatic group_header *group_hd; /* ptr to servers current group */
- Xstatic int group_is_set = 0; /* bool: t iff group_hd is set */
- Xstatic int try_again = 0; /* bool: t if timeout forces retry */
- Xstatic int can_post = 0; /* bool: t iff NNTP server accepts postings */
- X
- X#define ERR_TIMEOUT 503 /* Response code for timeout */
- X
- X/*
- X * debug_msg: print a debug message.
- X *
- X * The master appends prefix and str to a log file, and clients
- X * prints it as a message.
- X *
- X * This is controlled via the nntp-debug variable in nn, and
- X * the option -D2 (or -D3 if the normal -D option should also
- X * be turned on). Debug output from the master is written in
- X * $TMP/nnmaster.log.
- X */
- X
- Xstatic debug_msg(prefix, str)
- Xchar *prefix, *str;
- X{
- X static FILE *f = NULL;
- X
- X if (who_am_i == I_AM_MASTER) {
- X if (f == NULL) {
- X f = open_file(relative(tmp_directory, "nnmaster.log"), OPEN_CREATE);
- X if (f == NULL) {
- X nntp_debug = 0;
- X return;
- X }
- X }
- X fprintf(f, "%s %s\n", prefix, str);
- X fflush(f);
- X return;
- X }
- X
- X msg("NNTP%s %s", prefix, str);
- X user_delay(1);
- X}
- X
- X/*
- X * io_error: signal an I/O error in talking to the server.
- X *
- X * An nn client terminates a session with the user. The master
- X * simply closes the connection. The flag nntp_failed is set, for
- X * use by the master to terminate collection.
- X *
- X * BUG: if the nntp server is forcibly killed, errno can contain a
- X * bogus value, resulting in strange error messages. It is
- X * probably better just to write out the numerical value of errno.
- X */
- X
- Xstatic io_error()
- X{
- X if (who_am_i != I_AM_MASTER) {
- X user_error("Lost connection to NNTP server %s: %s", nntp_server, syserr());
- X /* NOTREACHED */
- X }
- X nntp_failed = 1;
- X if (is_connected) {
- X log_entry('N', "Lost connection to server %s: %s", nntp_server, syserr());
- X nntp_close_server();
- X }
- X}
- X
- X/*
- X * find_server: Find out which host to use as NNTP server.
- X *
- X * This is done by consulting the file NNTP_SERVER (defined in
- X * config.h). Set nntp_server[] to the host's name.
- X */
- X
- Xstatic void find_server()
- X{
- X char *cp, *name, *getenv();
- X char buf[BUFSIZ];
- X FILE *fp;
- X
- X /*
- X * This feature cannot normally be enabled, because the database and
- X * the users rc file contains references to articles by number, and
- X * these numbers are not unique across NNTP servers.
- X */
- X#ifdef DEBUG
- X if ((cp = getenv("NNTPSERVER")) != NULL) {
- X strncpy(nntp_server, cp, sizeof nntp_server);
- X return;
- X }
- X#endif /* DEBUG */
- X
- X name = NNTP_SERVER;
- X if (*name != '/')
- X name = relative(lib_directory, name);
- X
- X if ((fp = open_file(name, OPEN_READ)) != NULL) {
- X while (fgets(buf, sizeof buf, fp) != 0) {
- X if (*buf == '#' || *buf == '\n')
- X continue;
- X if ((cp = strchr(buf, '\n')) != 0)
- X *cp = '\0';
- X strncpy(nntp_server, buf, sizeof nntp_server);
- X fclose(fp);
- X return;
- X }
- X fclose(fp);
- X }
- X
- X if (who_am_i != I_AM_MASTER)
- X printf("\nCannot find name of NNTP server.\nCheck %s\n", name);
- X
- X sys_error("Failed to find name of NNTP server!");
- X}
- X
- X/*
- X * get_server_line: get a line from the server.
- X *
- X * Expects to be connected to the server.
- X * The line can be any kind of line, i.e., either response or text.
- X */
- X
- Xstatic get_server_line(string, size)
- X char *string;
- X int size;
- X{
- X register char *cp, *nl;
- X
- X if (fgets(string, size, nntp_in) == NULL) {
- X io_error();
- X return -1;
- X }
- X for (cp = string, nl = NULL; *cp != NUL; cp++) {
- X if (*cp == CR) {
- X nl = cp;
- X break;
- X }
- X if (nl == NULL && *cp == NL)
- X nl = cp;
- X }
- X if (nl != NULL) *nl = NUL;
- X
- X return 0;
- X}
- X
- X/*
- X * get_server: get a response line from the server.
- X *
- X * Expects to be connected to the server.
- X * Returns the numerical value of the reponse, or -1 in case of errors.
- X */
- X
- Xstatic get_server(string, size)
- X char *string;
- X int size;
- X{
- X if (get_server_line(string, size) < 0)
- X return -1;
- X
- X if (nntp_debug) debug_msg("<<<", string);
- X
- X return isdigit(*string) ? atoi(string) : 0;
- X}
- X
- X/*
- X * get_socket: get a connection to the nntp server.
- X *
- X * Doesn't return in case of errors.
- X */
- X
- Xstatic get_socket()
- X{
- X int s;
- X struct sockaddr_in sin;
- X struct servent *getservbyname(), *sp;
- X struct hostent *gethostbyname(), *hp;
- X#ifdef h_addr
- X int x = 0;
- X register char **cp;
- X#endif
- X
- X if ((sp = getservbyname("nntp", "tcp")) == NULL)
- X sys_error("nntp/tcp: Unknown service.\n");
- X
- X if ((hp = gethostbyname(nntp_server)) == NULL)
- X sys_error("NNTP server %s unknown.\n", nntp_server);
- X
- X bzero((char *) &sin, sizeof(sin));
- X sin.sin_family = hp->h_addrtype;
- X sin.sin_port = sp->s_port;
- X
- X#ifdef h_addr
- X /* get a socket and initiate connection -- use multiple addresses */
- X
- X for (cp = hp->h_addr_list; cp && *cp; cp++) {
- X s = socket(hp->h_addrtype, SOCK_STREAM, 0);
- X if (s < 0)
- X sys_error("Can't get NNTP socket: %s\n", syserr());
- X bcopy(*cp, (char *)&sin.sin_addr, hp->h_length);
- X
- X x = connect(s, (struct sockaddr *)&sin, sizeof (sin));
- X if (x == 0)
- X break;
- X if (who_am_i != I_AM_MASTER)
- X msg("Connecting to %s failed: %s", nntp_server, syserr());
- X (void) close(s);
- X s = -1;
- X }
- X if (x < 0 && who_am_i != I_AM_MASTER)
- X user_error("Giving up on NNTP server %s!\n", nntp_server);
- X#else /* no name server */
- X if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0)
- X sys_error("Can't get NNTP socket: %s\n", syserr());
- X
- X /* And then connect */
- X bcopy(hp->h_addr, (char *) &sin.sin_addr, hp->h_length);
- X if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
- X if (who_am_i == I_AM_MASTER)
- X sys_error("Connecting to %s failed: %s", nntp_server, syserr());
- X s = -1;
- X }
- X#endif
- X return s;
- X}
- X
- X/*
- X * connect_server: initialise a connection to the nntp server.
- X *
- X * It expects nntp_server[] to be set previously, by a call to
- X * nntp_check. It is called from nntp_get_article() and
- X * nntp_get_active() if there is no established connection.
- X */
- X
- Xstatic connect_server()
- X{
- X int sockt_rd, sockt_wr;
- X int response;
- X char line[NNTP_STRLEN];
- X
- X if (who_am_i != I_AM_MASTER && !silent)
- X msg("Connecting to NNTP server %s ... ", nntp_server);
- X
- X nntp_failed = 1;
- X is_connected = 0;
- X
- X sockt_rd = get_socket();
- X if (sockt_rd < 0)
- X return -1;
- X
- X if ((nntp_in = fdopen(sockt_rd, "r")) == NULL) {
- X close(sockt_rd);
- X return -1;
- X }
- X sockt_wr = dup(sockt_rd);
- X if ((nntp_out = fdopen(sockt_wr, "w")) == NULL) {
- X close(sockt_wr);
- X fclose(nntp_in);
- X nntp_in = NULL; /* from above */
- X return -1;
- X }
- X
- X /* Now get the server's signon message */
- X response = get_server(line, sizeof(line));
- X
- X if (who_am_i == I_AM_MASTER) {
- X if (response != OK_CANPOST && response != OK_NOPOST) {
- X log_entry('N', "Failed to connect to NNTP server");
- X log_entry('N', "Response: %s", line);
- X fclose(nntp_out);
- X fclose(nntp_in);
- X return -1;
- X }
- X } else {
- X switch (response) {
- X case OK_CANPOST:
- X can_post = 1;
- X break;
- X case OK_NOPOST:
- X can_post = 0;
- X break;
- X default:
- X user_error(line);
- X /* NOTREACHED */
- X }
- X }
- X if (who_am_i != I_AM_MASTER && !silent)
- X msg("Connecting to NNTP server %s ... ok (%s)",
- X nntp_server, can_post ? "posting is allowed" : "no posting");
- X
- X is_connected = 1;
- X group_is_set = 0;
- X nntp_failed = 0;
- X try_again = 0;
- X return 0;
- X}
- X
- X
- X/*
- X * put_server: send a line to the nntp server.
- X *
- X * Expects to be connected to the server.
- X */
- X
- Xstatic put_server(string)
- X char *string;
- X{
- X if (nntp_debug) debug_msg(">>>", string);
- X
- X fprintf(nntp_out, "%s\r\n", string);
- X if (fflush(nntp_out) == EOF) {
- X io_error();
- X return -1;
- X }
- X return 0;
- X}
- X
- X/*
- X * ask_server: ask the server a question and return the answer.
- X *
- X * Expects to be connected to the server.
- X * Returns the numerical value of the reponse, or -1 in case of
- X * errors.
- X * Contains some code to handle server timeouts intelligently.
- X */
- X
- X/*VARARGS*/
- Xstatic ask_server(va_alist)
- Xva_dcl
- X{
- X char buf[NNTP_STRLEN];
- X char *fmt;
- X int response;
- X use_vararg;
- X
- X start_vararg;
- X fmt = va_arg1(char *);
- X vsprintf(buf, fmt, va_args2toN);
- X end_vararg;
- X
- X if (put_server(buf) < 0)
- X return -1;
- X response = get_server(buf, sizeof(buf));
- X
- X /*
- X * Handle the response from the server. Responses are handled as
- X * followes:
- X *
- X * 100-199 Informational. Passed back. (should they be ignored?).
- X * 200-299 Ok messages. Passed back.
- X * 300-399 Ok and proceed. Can not happen in nn.
- X * 400-499 Errors (no article, etc). Passed up and handled there.
- X * 500-599 Fatal NNTP errors. Handled below.
- X */
- X if (response == ERR_GOODBYE || response > ERR_COMMAND) {
- X nntp_failed = 1;
- X nntp_close_server();
- X
- X if (response != ERR_TIMEOUT) { /* if not timeout, complain */
- X sys_error("NNTP %s response: %d", buf, response);
- X /* NOTREACHED */
- X }
- X try_again = 1;
- X group_is_set = 0;
- X }
- X return response;
- X}
- X
- X/*
- X * copy_text: copy text response into file.
- X *
- X * Copies a text response into an open file.
- X * Return -1 on error, 0 otherwise. It is treated as an error, if
- X * the returned response it not what was expected.
- X */
- X
- Xstatic int last_copy_blank;
- X
- Xstatic copy_text(fp)
- Xregister FILE *fp;
- X{
- X char buf[NNTP_STRLEN];
- X register char *cp;
- X register int nlines;
- X
- X nlines = 0;
- X last_copy_blank = 0;
- X while (get_server_line(buf, sizeof buf) >= 0) {
- X cp = buf;
- X if (*cp == '.')
- X if (*++cp == '\0') {
- X if (nlines <= 0) break;
- X if (nntp_debug) {
- X sprintf(buf, "%d lines", nlines);
- X debug_msg("COPY", buf);
- X }
- X return 0;
- X }
- X fputs(cp, fp);
- X last_copy_blank = (*cp == NUL);
- X putc('\n', fp);
- X nlines++;
- X }
- X fclose(fp);
- X if (nntp_debug) debug_msg("COPY", "EMPTY");
- X return -1;
- X}
- X
- X
- Xstatic do_set_group()
- X{
- X int n;
- X
- X switch (n = ask_server("GROUP %s", group_hd->group_name)) {
- X case OK_GROUP:
- X group_is_set = 1;
- X return 1;
- X
- X case ERR_NOGROUP:
- X log_entry('N', "NNTP: group %s not found", group_hd->group_name);
- X return -1;
- X
- X default:
- X if (try_again) return 0; /* Handle nntp server timeouts */
- X break;
- X }
- X if (!nntp_failed) {
- X log_entry('N', "GROUP %s response: %d", group_hd->group_name, n);
- X nntp_failed = 1;
- X }
- X return -1;
- X}
- X
- X/*
- X * The following functions implements a simple lru cache of recently
- X * accessed articles. It is a simple way to improve effeciency. Files
- X * must be kept by name, because the rest of the code expects to be able
- X * to open an article multiple times, and get separate file pointers.
- X */
- X
- Xstruct cache {
- X char *file_name; /* file name */
- X article_number art; /* article stored in file */
- X group_header *grp; /* from this group */
- X unsigned time; /* time last accessed */
- X} cache[NNTPCACHE];
- X
- Xstatic unsigned time_counter = 1; /* virtual time */
- X
- X/*
- X * search_cache: search the cache for an (article, group) pair.
- X *
- X * Returns a pointer to the slot where it is, null otherwise
- X */
- X
- Xstatic struct cache *search_cache(art, gh)
- X article_number art;
- X group_header *gh;
- X{
- X struct cache *cptr = cache;
- X int i;
- X
- X if (who_am_i == I_AM_MASTER) return NULL;
- X
- X if (nntp_cache_size > NNTPCACHE) nntp_cache_size = NNTPCACHE;
- X
- X for (i = 0; i < nntp_cache_size; i++, cptr++)
- X if (cptr->art == art && cptr->grp == gh) {
- X cptr->time = time_counter++;
- X return cptr;
- X }
- X return NULL;
- X}
- X
- X/*
- X * new_cache_slot: get a free cache slot.
- X *
- X * Returns a pointer to the allocated slot.
- X * Frees the old filename, and allocates a new, unused filename.
- X * Cache files can also stored in a common directory defined in
- X * ~/.nn or CACHE_DIRECTORY if defined in config.h.
- X */
- X
- Xstatic struct cache *new_cache_slot()
- X{
- X register struct cache *cptr = cache;
- X int i, lru;
- X unsigned min_time = time_counter;
- X char name[FILENAME];
- X
- X if (nntp_cache_dir == NULL) {
- X#ifdef CACHE_DIRECTORY
- X nntp_cache_dir = CACHE_DIRECTORY;
- X#else
- X if (who_am_i == I_AM_MASTER)
- X nntp_cache_dir = db_directory;
- X else
- X nntp_cache_dir = nn_directory;
- X#endif
- X }
- X
- X if (who_am_i == I_AM_MASTER) {
- X cptr = &cache[0];
- X if (cptr->file_name == NULL)
- X cptr->file_name = mk_file_name(nntp_cache_dir, "master_cache");
- X return cptr;
- X }
- X
- X for (i = 0; i < nntp_cache_size; i++, cptr++)
- X if (min_time > cptr->time) {
- X min_time = cptr->time;
- X lru = i;
- X }
- X cptr = &cache[lru];
- X
- X if (cptr->file_name == NULL) {
- X sprintf(name, "%s/nn-%d.%02d~", nntp_cache_dir, process_id, lru);
- X cptr->file_name = copy_str(name);
- X } else
- X unlink(cptr->file_name);
- X
- X cptr->time = time_counter++;
- X return cptr;
- X}
- X
- X/*
- X * clean_cache: clean up the cache.
- X *
- X * Removes all allocated files.
- X */
- X
- Xstatic void clean_cache()
- X{
- X struct cache *cptr = cache;
- X int i;
- X
- X for (i = 0; i < nntp_cache_size; i++, cptr++)
- X if (cptr->file_name)
- X unlink(cptr->file_name);
- X}
- X
- X/*
- X * nntp_check: Find out whether we need to use NNTP.
- X *
- X * This is done by comparing the NNTP servers name with whatever
- X * gethostname() returns.
- X * use_nntp and news_active are initialised as a side effect.
- X */
- X
- Xnntp_check()
- X{
- X char host[128];
- X
- X if (nntp_local_server) return;
- X
- X find_server();
- X gethostname(host, sizeof host);
- X use_nntp = strcmp(host, nntp_server) != 0; /* too simplistic ??? */
- X
- X if (use_nntp) {
- X freeobj(news_active);
- X news_active = mk_file_name(db_directory, "ACTIVE");
- X }
- X}
- X
- X/*
- X * nntp_no_post: Check to see whether posting is allowed.
- X */
- X
- Xnntp_no_post()
- X{
- X if (!is_connected && connect_server() < 0)
- X return 1; /* If we cannot connect, neither can inews */
- X if (can_post == 0) {
- X msg("NNTP server does not allow postings from this host. Sorry!");
- X return 1;
- X }
- X return 0;
- X}
- X
- X
- X/*
- X * nntp_set_group: set the server's current group.
- X *
- X * Actual communication is delayed until an article is accessed, to
- X * avoid unnecessary traffic.
- X */
- X
- Xnntp_set_group(gh)
- X group_header *gh;
- X{
- X group_hd = gh;
- X group_is_set = 0;
- X return 0;
- X}
- X
- X/*
- X * nntp_get_active: get a copy of the active file.
- X *
- X * If we are the master get a copy of the file from the nntp server.
- X * nnadmin just uses the one we already got. In this way the master
- X * can maintain a remote copy of the servers active file.
- X * We try to be a little smart, if not inefficient, about the
- X * modification times on the local active file.
- X * Even when the master is running on the nntp server, a separate
- X * copy of the active file will be made for access via NFS.
- X */
- X
- Xnntp_get_active()
- X{
- X FILE *old, *new;
- X char bufo[NNTP_STRLEN], bufn[NNTP_STRLEN];
- X char *new_name;
- X int same, n;
- X
- X if (who_am_i != I_AM_MASTER)
- X return access(news_active, 4);
- X
- X again:
- X if (!is_connected && connect_server() < 0)
- X return -1;
- X
- X new_name = mktemp(relative(db_directory, ".actXXXXXX"));
- X
- X switch (n = ask_server("LIST")) {
- X case OK_GROUPS:
- X new = open_file(new_name, OPEN_CREATE_RW|MUST_EXIST);
- X if (copy_text(new) == 0) break;
- X unlink(new_name);
- X if (!nntp_failed) {
- X log_entry('N', "LIST empty");
- X nntp_failed = 1;
- X }
- X return -1;
- X default:
- X if (try_again) goto again; /* Handle nntp server timeouts */
- X log_entry('N', "LIST response: %d", n);
- X return -1;
- X }
- X
- X rewind(new);
- X same = 0;
- X if ((old = open_file(news_active, OPEN_READ)) != NULL) {
- X do {
- X fgets(bufo, sizeof bufo, old);
- X fgets(bufn, sizeof bufn, new);
- X } while (!feof(old) && !feof(new) && strcmp(bufo, bufn) == 0);
- X same = feof(old) && feof(new);
- X fclose(old);
- X }
- X fclose(new);
- X
- X if (same)
- X unlink(new_name);
- X else
- X if (rename(new_name, news_active) != 0)
- X sys_error("Cannot rename %s to %s", new_name, news_active);
- X
- X return 0;
- X}
- X
- X/*
- X * nntp_get_article_list: get list of all article numbers in group
- X *
- X * Sends XHDR command to the server, and parses the following
- X * text response to get a list of article numbers which is saved
- X * in a list and returned.
- X * Return NULL on error. It is treated as an error, if
- X * the returned response it not what was expected.
- X */
- X
- Xstatic article_number *article_list = NULL;
- Xstatic long art_list_length = 0;
- X
- Xstatic sort_art_list(f1, f2)
- Xregister article_number *f1, *f2;
- X{
- X return (*f1 < *f2) ? -1 : (*f1 == *f2) ? 0 : 1;
- X}
- X
- Xarticle_number *nntp_get_article_list(gh)
- Xgroup_header *gh;
- X{
- X char buf[NNTP_STRLEN];
- X register article_number *art;
- X register char *cp;
- X register long count = 0; /* No. of completions plus one */
- X int n;
- X static int try_listgroup = 1;
- X
- X again:
- X if (!is_connected && connect_server() < 0)
- X return NULL;
- X
- X /* it is really an extreme waste of time to use XHDR since all we */
- X /* are interested in is the article numbers (as we do locally). */
- X /* If somebody hacks up an nntp server that understands LISTGROUP */
- X /* they will get much less load on the nntp server */
- X /* It should simply return the existing article numbers is the group*/
- X /* -- they don't even have to be sorted (only XHDR needs that) */
- X
- X if (try_listgroup) {
- X switch (n = ask_server("LISTGROUP %s", group_hd->group_name)) {
- X case OK_GROUP:
- X break;
- X default:
- X if (try_again) goto again; /* Handle nntp server timeouts */
- X log_entry('N', "LISTGROUP response: %d", n);
- X case ERR_COMMAND:
- X try_listgroup = 0;
- X goto again; /* error may have closed down server connection */
- X }
- X }
- X if (!try_listgroup) {
- X if (group_is_set == 0)
- X switch (do_set_group()) {
- X case -1:
- X return NULL;
- X case 0:
- X goto again;
- X case 1:
- X break;
- X }
- X
- X switch (n = ask_server("XHDR message-id %ld-%ld",
- X (long)gh->first_db_article, (long)gh->last_db_article)) {
- X case OK_HEAD:
- X break;
- X default:
- X if (try_again) goto again; /* Handle nntp server timeouts */
- X log_entry('N', "XHDR response: %d", n);
- X case ERR_COMMAND:
- X nntp_failed = 2;
- X return NULL;
- X }
- X }
- X
- X count = 0;
- X art = article_list;
- X
- X while (get_server_line(buf, sizeof buf) >= 0) {
- X cp = buf;
- X if (*cp == '.' && *++cp == '\0') break;
- X
- X if (count == art_list_length) {
- X art_list_length += 250;
- X article_list = resizeobj(article_list, article_number, art_list_length + 1);
- X art = article_list + count;
- X }
- X *art++ = atol(cp);
- X count++;
- X }
- X *art = 0;
- X
- X if (try_listgroup)
- X quicksort(article_list, count, article_number, sort_art_list);
- X *art = 0;
- X return article_list;
- X}
- X
- X/*
- X * nntp_get_article: get an article from the server.
- X *
- X * Returns a FILE pointer.
- X * If necessary the server's current group is set.
- X * The article (header and body) are copied into a file, so they
- X * are seekable (nn likes that).
- X */
- X
- Xstatic char *mode_cmd[] = {
- X "ARTICLE",
- X "HEAD",
- X "BODY"
- X};
- X
- XFILE *nntp_get_article(article, mode)
- Xarticle_number article;
- Xint mode; /* 0 => whole article, 1 => head only, 2 => body only */
- X{
- X FILE *tmp;
- X static struct cache *cptr;
- X int n;
- X
- X again:
- X if (!is_connected && connect_server() < 0) {
- X return NULL;
- X }
- X
- X /*
- X * Set the server group to the current group
- X */
- X if (group_is_set == 0)
- X switch (do_set_group()) {
- X case -1:
- X return NULL;
- X case 0:
- X goto again;
- X case 1:
- X break;
- X }
- X
- X /*
- X * Search the cache for the requested article, and allocate a new
- X * slot if necessary (if appending body, we already got it).
- X */
- X
- X if (mode != 2) {
- X cptr = search_cache(article, group_hd);
- X if (cptr != NULL) goto out;
- X cptr = new_cache_slot();
- X }
- X
- X /*
- X * Copy the article.
- X */
- X switch (n = ask_server("%s %ld", mode_cmd[mode], (long)article)) {
- X case OK_ARTICLE:
- X case OK_HEAD:
- X tmp = open_file(cptr->file_name, OPEN_CREATE|MUST_EXIST);
- X if (copy_text(tmp) < 0)
- X return NULL;
- X
- X if (mode == 1 && !last_copy_blank)
- X fputc(NL, tmp); /* add blank line after header */
- X
- X cptr->art = article;
- X cptr->grp = group_hd;
- X fclose(tmp);
- X goto out;
- X
- X case OK_BODY:
- X tmp = open_file(cptr->file_name, OPEN_APPEND|MUST_EXIST);
- X fseek(tmp, (off_t)0, 2);
- X if (copy_text(tmp) < 0)
- X return NULL;
- X fclose(tmp);
- X goto out;
- X
- X case ERR_NOARTIG:
- X return NULL;
- X
- X default:
- X if (try_again) goto again; /* Handle nntp server timeouts */
- X log_entry('N', "ARTICLE %ld response: %d", (long)article, n);
- X nntp_failed = 1;
- X return NULL;
- X }
- X
- X out:
- X return open_file(cptr->file_name, OPEN_READ|MUST_EXIST);
- X}
- X
- X/*
- X * Return local file name holding article
- X */
- X
- Xchar *nntp_get_filename(art, gh)
- Xarticle_number art;
- Xgroup_header *gh;
- X{
- X struct cache *cptr;
- X
- X cptr = search_cache(art, gh);
- X
- X return cptr == NULL ? NULL : cptr->file_name;
- X}
- X
- X/*
- X * nntp_close_server: close the connection to the server.
- X */
- X
- Xnntp_close_server()
- X{
- X if (!is_connected)
- X return;
- X
- X if (!nntp_failed) { /* avoid infinite recursion */
- X int n;
- X
- X n = ask_server("QUIT");
- X if (n != OK_GOODBYE)
- X ; /* WHAT NOW ??? */
- X }
- X
- X (void) fclose(nntp_out);
- X (void) fclose(nntp_in);
- X
- X is_connected = 0;
- X}
- X
- X/*
- X * nntp_cleanup: clean up after an nntp session.
- X *
- X * Called from nn_exit().
- X */
- X
- Xnntp_cleanup()
- X{
- X if (is_connected)
- X nntp_close_server();
- X clean_cache();
- X}
- X#endif /* NNTP */
- X
- END_OF_FILE
- if test 22676 -ne `wc -c <'nntp.c'`; then
- echo shar: \"'nntp.c'\" unpacked with wrong size!
- fi
- # end of 'nntp.c'
- fi
- echo shar: End of archive 10 \(of 22\).
- cp /dev/null ark10isdone
- MISSING=""
- for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 ; do
- if test ! -f ark${I}isdone ; then
- MISSING="${MISSING} ${I}"
- fi
- done
- if test "${MISSING}" = "" ; then
- echo You have unpacked all 22 archives.
- rm -f ark[1-9]isdone ark[1-9][0-9]isdone
- else
- echo You still must unpack the following archives:
- echo " " ${MISSING}
- fi
- exit 0
-
- exit 0 # Just in case...
-